5
This commit is contained in:
215
backend.py
215
backend.py
@@ -1421,6 +1421,215 @@ def check_and_restore_kernel(mount_point: str, distro_type: str, has_separate_bo
|
||||
return False, f"检查 /boot 目录失败: {e}"
|
||||
|
||||
|
||||
def restore_bls_entries(mount_point: str, distro_type: str) -> Tuple[bool, str]:
|
||||
"""
|
||||
恢复 BLS (Boot Loader Specification) 配置条目。
|
||||
CentOS/RHEL/Fedora 等使用 BLS 格式,启动项在 /boot/loader/entries/*.conf
|
||||
|
||||
返回: (是否成功, 错误信息)
|
||||
"""
|
||||
# 检查是否使用 BLS
|
||||
loader_dir = os.path.join(mount_point, "boot/loader/entries")
|
||||
grub_cfg_path = os.path.join(mount_point, "boot/grub2/grub.cfg")
|
||||
|
||||
# 检查 grub.cfg 是否包含 blscfg
|
||||
uses_bls = False
|
||||
try:
|
||||
if os.path.exists(grub_cfg_path):
|
||||
with open(grub_cfg_path, 'r') as f:
|
||||
content = f.read()
|
||||
if 'blscfg' in content or 'BootLoaderSpec' in content:
|
||||
uses_bls = True
|
||||
log_info("检测到系统使用 BLS (Boot Loader Specification)")
|
||||
except Exception as e:
|
||||
log_debug(f"检查 BLS 失败: {e}")
|
||||
|
||||
if not uses_bls:
|
||||
log_debug("系统不使用 BLS,跳过 BLS 恢复")
|
||||
return True, ""
|
||||
|
||||
log_step("恢复 BLS 启动条目")
|
||||
|
||||
chroot_cmd_prefix = ["sudo", "chroot", mount_point]
|
||||
|
||||
# 方法1: 使用 kernel-install 重新生成 BLS 条目(推荐)
|
||||
log_info("尝试使用 kernel-install 重新生成 BLS 条目...")
|
||||
|
||||
# 获取已安装的内核版本
|
||||
boot_dir = os.path.join(mount_point, "boot")
|
||||
kernel_versions = []
|
||||
|
||||
try:
|
||||
for f in os.listdir(boot_dir):
|
||||
if f.startswith("vmlinuz-"):
|
||||
kernel_ver = f.replace("vmlinuz-", "")
|
||||
kernel_versions.append(kernel_ver)
|
||||
except Exception as e:
|
||||
log_warning(f"读取 /boot 目录失败: {e}")
|
||||
|
||||
if not kernel_versions:
|
||||
log_error("没有找到内核版本,无法生成 BLS 条目")
|
||||
return False, "没有内核版本"
|
||||
|
||||
log_info(f"发现内核版本: {kernel_versions}")
|
||||
|
||||
# 创建 loader 目录
|
||||
os.makedirs(loader_dir, exist_ok=True)
|
||||
|
||||
for kernel_ver in kernel_versions:
|
||||
# 检查是否已有 BLS 条目
|
||||
entry_file = os.path.join(loader_dir, f"{kernel_ver}.conf")
|
||||
if os.path.exists(entry_file):
|
||||
log_info(f"BLS 条目已存在: {entry_file}")
|
||||
continue
|
||||
|
||||
log_info(f"为内核 {kernel_ver} 生成 BLS 条目...")
|
||||
|
||||
# 尝试使用 kernel-install
|
||||
success, _, stderr = run_command(
|
||||
chroot_cmd_prefix + ["kernel-install", "add", kernel_ver, f"/boot/vmlinuz-{kernel_ver}"],
|
||||
f"生成 BLS 条目 for {kernel_ver}",
|
||||
timeout=30
|
||||
)
|
||||
|
||||
if success:
|
||||
log_success(f"✓ kernel-install 成功: {kernel_ver}")
|
||||
else:
|
||||
log_warning(f"kernel-install 失败,尝试手动创建 BLS 条目...")
|
||||
|
||||
# 方法2: 手动创建 BLS 条目
|
||||
machine_id = ""
|
||||
try:
|
||||
machine_id_path = os.path.join(mount_point, "etc/machine-id")
|
||||
if os.path.exists(machine_id_path):
|
||||
with open(machine_id_path, 'r') as f:
|
||||
machine_id = f.read().strip()
|
||||
except:
|
||||
pass
|
||||
|
||||
if not machine_id:
|
||||
machine_id = "unknown"
|
||||
|
||||
# 读取 /etc/os-release 获取标题
|
||||
os_name = "Linux"
|
||||
os_version = ""
|
||||
try:
|
||||
os_release_path = os.path.join(mount_point, "etc/os-release")
|
||||
if os.path.exists(os_release_path):
|
||||
with open(os_release_path, 'r') as f:
|
||||
for line in f:
|
||||
if line.startswith('NAME='):
|
||||
os_name = line.split('=')[1].strip().strip('"')
|
||||
elif line.startswith('VERSION_ID='):
|
||||
os_version = line.split('=')[1].strip().strip('"')
|
||||
except:
|
||||
pass
|
||||
|
||||
title = f"{os_name}"
|
||||
if os_version:
|
||||
title += f" {os_version}"
|
||||
|
||||
# 获取根分区 UUID 或路径
|
||||
root_device = "/dev/mapper/cl-root" # 默认,应该根据实际检测
|
||||
|
||||
# 尝试从 /etc/fstab 获取根分区
|
||||
try:
|
||||
fstab_path = os.path.join(mount_point, "etc/fstab")
|
||||
if os.path.exists(fstab_path):
|
||||
with open(fstab_path, 'r') as f:
|
||||
for line in f:
|
||||
if line.startswith('/') and ' / ' in line:
|
||||
parts = line.split()
|
||||
if len(parts) >= 2 and parts[1] == '/':
|
||||
root_device = parts[0]
|
||||
break
|
||||
except:
|
||||
pass
|
||||
|
||||
# 创建 BLS 条目文件
|
||||
bls_content = f"""title {title} ({kernel_ver})
|
||||
version {kernel_ver}
|
||||
linux /vmlinuz-{kernel_ver}
|
||||
initrd /initramfs-{kernel_ver}.img
|
||||
options root={root_device} ro
|
||||
"""
|
||||
|
||||
# 对于 CentOS/RHEL,添加特定的 LVM 和 resume 参数
|
||||
if distro_type in ["centos", "rhel", "fedora", "rocky", "almalinux"]:
|
||||
bls_content = f"""title {title} ({kernel_ver})
|
||||
version {kernel_ver}
|
||||
linux /vmlinuz-{kernel_ver}
|
||||
initrd /initramfs-{kernel_ver}.img
|
||||
options root={root_device} ro crashkernel=auto resume=/dev/mapper/cl-swap rd.lvm.lv=cl/root rd.lvm.lv=cl/swap rhgb quiet
|
||||
"""
|
||||
|
||||
entry_filename = f"{machine_id}-{kernel_ver}.conf"
|
||||
entry_path = os.path.join(loader_dir, entry_filename)
|
||||
|
||||
try:
|
||||
with open(entry_path, 'w') as f:
|
||||
f.write(bls_content)
|
||||
log_success(f"✓ 手动创建 BLS 条目: {entry_path}")
|
||||
except Exception as e:
|
||||
log_error(f"创建 BLS 条目失败: {e}")
|
||||
return False, f"创建 BLS 条目失败: {e}"
|
||||
|
||||
# 方法3: 重新运行 grub2-mkconfig 确保 BLS 支持正确
|
||||
log_info("重新生成 grub.cfg 以确保 BLS 支持...")
|
||||
|
||||
# 找到 grub2-mkconfig 或 grub-mkconfig
|
||||
mkconfig_cmd = None
|
||||
for cmd in ["/usr/sbin/grub2-mkconfig", "/sbin/grub2-mkconfig", "/usr/bin/grub2-mkconfig"]:
|
||||
if os.path.exists(os.path.join(mount_point, cmd.lstrip('/'))):
|
||||
mkconfig_cmd = cmd
|
||||
break
|
||||
|
||||
if not mkconfig_cmd:
|
||||
for cmd in ["/usr/sbin/grub-mkconfig", "/sbin/grub-mkconfig", "/usr/bin/grub-mkconfig"]:
|
||||
if os.path.exists(os.path.join(mount_point, cmd.lstrip('/'))):
|
||||
mkconfig_cmd = cmd
|
||||
break
|
||||
|
||||
if mkconfig_cmd:
|
||||
# 找到 grub.cfg 路径
|
||||
grub_cfg = "/boot/grub2/grub.cfg"
|
||||
for cfg_path in ["/boot/grub2/grub.cfg", "/boot/grub/grub.cfg"]:
|
||||
if os.path.exists(os.path.join(mount_point, cfg_path.lstrip('/'))):
|
||||
grub_cfg = cfg_path
|
||||
break
|
||||
|
||||
success, _, stderr = run_command(
|
||||
chroot_cmd_prefix + [mkconfig_cmd, "-o", grub_cfg],
|
||||
"重新生成 grub.cfg",
|
||||
timeout=60
|
||||
)
|
||||
|
||||
if success:
|
||||
log_success("✓ grub.cfg 重新生成成功")
|
||||
else:
|
||||
log_warning(f"grub.cfg 重新生成失败: {stderr}")
|
||||
|
||||
# 最终检查
|
||||
try:
|
||||
if os.path.exists(loader_dir):
|
||||
entries = [f for f in os.listdir(loader_dir) if f.endswith('.conf')]
|
||||
log_info(f"BLS 条目数量: {len(entries)}")
|
||||
for entry in entries:
|
||||
log_info(f" - {entry}")
|
||||
|
||||
if len(entries) > 0:
|
||||
log_success("✓ BLS 配置恢复完成")
|
||||
return True, ""
|
||||
else:
|
||||
log_warning("没有 BLS 条目生成")
|
||||
return False, "BLS 条目生成失败"
|
||||
else:
|
||||
log_error("loader/entries 目录不存在")
|
||||
return False, "BLS 目录不存在"
|
||||
except Exception as e:
|
||||
return False, f"检查 BLS 条目失败: {e}"
|
||||
|
||||
|
||||
def chroot_and_repair_grub(mount_point: str, target_disk: str,
|
||||
is_uefi: bool = False, distro_type: str = "unknown",
|
||||
install_hybrid: bool = False,
|
||||
@@ -1473,6 +1682,12 @@ def chroot_and_repair_grub(mount_point: str, target_disk: str,
|
||||
log_error(f"内核恢复失败: {kernel_err}")
|
||||
return False, f"内核恢复失败: {kernel_err}"
|
||||
|
||||
# 恢复 BLS 启动条目(CentOS/RHEL/Fedora 使用 BLS)
|
||||
bls_ok, bls_err = restore_bls_entries(mount_point, distro_type)
|
||||
if not bls_ok:
|
||||
log_warning(f"BLS 恢复失败: {bls_err}")
|
||||
# BLS 失败不终止,继续尝试传统菜单
|
||||
|
||||
chroot_cmd_prefix = ["sudo", "chroot", mount_point]
|
||||
|
||||
# 检测 Live 环境
|
||||
|
||||
Reference in New Issue
Block a user