Redo backend installer logic to support BIOS/MBR boot on older devices and switch to GRUB2

This commit is contained in:
2026-02-09 15:02:14 +01:00
parent b37fc8d060
commit e8a2f7aaa9
7 changed files with 187 additions and 202 deletions

View File

@@ -110,6 +110,10 @@ def mount_pseudo_fs(mount_root):
logger.warning(f"Failed to unmount {path}: {e}")
def is_uefi():
return os.path.exists("/sys/firmware/efi")
def install_minimal_os(mount_root, releasever="43"):
"""
Installs minimal Fedora packages to mount_root.
@@ -117,22 +121,29 @@ def install_minimal_os(mount_root, releasever="43"):
logger.info(f"Installing minimal Fedora {releasever} to {mount_root}...")
log_os_install("INSTALL", "start", f"Target: {mount_root}, Release: {releasever}")
uefi = is_uefi()
packages = [
"basesystem",
"bash",
"coreutils",
"kernel",
"systemd",
"systemd-boot-unsigned",
"dnf",
"shadow-utils",
"util-linux",
"efibootmgr",
"passwd",
"rootfiles",
"vim-minimal",
"grub2-tools",
"grubby",
]
if uefi:
packages += ["grub2-efi-x64", "shim-x64", "efibootmgr"]
else:
packages += ["grub2-pc"]
# Offline installation logic
possible_repos = [
"/run/install/repo",
@@ -175,7 +186,7 @@ def install_minimal_os(mount_root, releasever="43"):
"--cacheonly",
]
else:
logger.warning("ISO repository not found in common locations. DNF might try to use network.")
logger.warning("ISO repository not found. DNF might try to use network.")
dnf_args = []
cmd = [
@@ -200,56 +211,48 @@ def install_minimal_os(mount_root, releasever="43"):
log_os_install("INSTALL", "complete", f"Installed to {mount_root}")
def configure_system(mount_root, partition_info, user_info=None):
def configure_system(mount_root, partition_info, user_info=None, disk_device=None):
"""
Basic configuration: fstab, systemd-boot, and user creation.
Basic configuration: fstab, grub2, and user creation.
"""
logger.info("Configuring system...")
log_os_install("CONFIGURE", "start", f"Configuring system in {mount_root}")
uefi = is_uefi()
# 1. Generate fstab
def get_uuid(dev):
res = run_command(["blkid", "-s", "UUID", "-o", "value", dev])
return res.stdout.strip()
root_uuid = get_uuid(partition_info["root"])
efi_uuid = get_uuid(partition_info["efi"])
fstab_lines = [f"UUID={root_uuid} / ext4 defaults 1 1"]
if uefi and partition_info.get("efi"):
efi_uuid = get_uuid(partition_info["efi"])
fstab_lines.append(f"UUID={efi_uuid} /boot/efi vfat defaults 0 2")
swap_entry = ""
if partition_info.get("swap"):
swap_uuid = get_uuid(partition_info["swap"])
swap_entry = f"UUID={swap_uuid} none swap defaults 0 0\n"
fstab_lines.append(f"UUID={swap_uuid} none swap defaults 0 0")
fstab_content = f"""
UUID={root_uuid} / ext4 defaults 1 1
UUID={efi_uuid} /boot vfat defaults 0 2
{swap_entry}
"""
os.makedirs(os.path.join(mount_root, "etc"), exist_ok=True)
with open(os.path.join(mount_root, "etc/fstab"), "w") as f:
f.write(fstab_content)
f.write("\n".join(fstab_lines) + "\n")
with mount_pseudo_fs(mount_root):
# 2. Configure User
if user_info:
logger.info(f"Creating user {user_info['username']}...")
# Create user and add to wheel group (sudoer)
run_command(["chroot", mount_root, "useradd", "-m", "-G", "wheel", user_info["username"]])
# Ensure changes to /etc/passwd and /etc/shadow are flushed
run_command(["sync"])
# Ensure wheel group has sudo privileges
sudoers_dir = os.path.join(mount_root, "etc/sudoers.d")
os.makedirs(sudoers_dir, exist_ok=True)
with open(os.path.join(sudoers_dir, "wheel"), "w") as f:
f.write("%wheel ALL=(ALL) ALL\n")
os.chmod(os.path.join(sudoers_dir, "wheel"), 0o440)
# Set hostname
with open(os.path.join(mount_root, "etc/hostname"), "w") as f:
f.write(user_info["hostname"] + "\n")
# Set user and root password using hashed passwords and usermod
# Set passwords
try:
logger.info(f"Setting hashed passwords for {user_info['username']} and root...")
# Generate SHA512 hash using openssl on the host
res = subprocess.run(
["openssl", "passwd", "-6", user_info["password"]],
capture_output=True,
@@ -257,64 +260,38 @@ UUID={efi_uuid} /boot vfat defaults 0 2
check=True
)
hashed_pass = res.stdout.strip()
# Apply hash to user and root using usermod -p (takes encrypted password)
run_command(["chroot", mount_root, "usermod", "-p", hashed_pass, user_info["username"]])
run_command(["chroot", mount_root, "usermod", "-p", hashed_pass, "root"])
run_command(["sync"])
except Exception as e:
logger.error(f"Failed to set passwords: {e}")
raise e
# Set hostname
with open(os.path.join(mount_root, "etc/hostname"), "w") as f:
f.write(user_info["hostname"] + "\n")
# 3. Configure systemd-boot
# Ensure machine-id exists for kernel-install
if not os.path.exists(os.path.join(mount_root, "etc/machine-id")):
run_command(["chroot", mount_root, "systemd-machine-id-setup"])
# Set kernel command line
os.makedirs(os.path.join(mount_root, "etc/kernel"), exist_ok=True)
with open(os.path.join(mount_root, "etc/kernel/cmdline"), "w") as f:
f.write(f"root=UUID={root_uuid} rw quiet\n")
# 3. Configure GRUB2
logger.info("Configuring GRUB2...")
# Set kernel layout to BLS for systemd-boot
with open(os.path.join(mount_root, "etc/kernel/layout"), "w") as f:
f.write("bls\n")
# Ensure /etc/default/grub exists
grub_default = os.path.join(mount_root, "etc/default/grub")
if not os.path.exists(grub_default):
with open(grub_default, "w") as f:
f.write('GRUB_TIMEOUT=5\nGRUB_DISTRIBUTOR="$(sed \'s, release .*$,,g\' /etc/system-release)"\nGRUB_DEFAULT=saved\nGRUB_DISABLE_SUBMENU=true\nGRUB_TERMINAL_OUTPUT="console"\nGRUB_CMDLINE_LINUX="rhgb quiet"\nGRUB_DISABLE_RECOVERY="true"\nGRUB_ENABLE_BLSCFG=true\n')
# Install systemd-boot to the ESP (mounted at /boot)
# Note: removed --force as it's not supported by all bootctl versions
run_command(["chroot", mount_root, "bootctl", "install", "--path=/boot"])
if uefi:
run_command(["chroot", mount_root, "grub2-install", "--target=x86_64-efi", "--efi-directory=/boot/efi", "--bootloader-id=iridium", "--recheck"])
run_command(["chroot", mount_root, "grub2-mkconfig", "-o", "/boot/efi/EFI/iridium/grub.cfg"])
# Fedora compatibility link
fedora_dir = os.path.join(mount_root, "boot/efi/EFI/fedora")
os.makedirs(fedora_dir, exist_ok=True)
with open(os.path.join(fedora_dir, "grub.cfg"), "w") as f:
f.write(f"configfile /EFI/iridium/grub.cfg\n")
else:
if not disk_device:
# Try to guess disk device from root partition
disk_device = partition_info["root"].rstrip("0123456789")
if disk_device.endswith("p"): disk_device = disk_device[:-1]
logger.info(f"Installing GRUB to {disk_device} (BIOS)")
run_command(["chroot", mount_root, "grub2-install", "--target=i386-pc", disk_device])
run_command(["chroot", mount_root, "grub2-mkconfig", "-o", "/boot/grub2/grub.cfg"])
# Add kernel entries
modules_dir = os.path.join(mount_root, "lib/modules")
if os.path.exists(modules_dir):
kvers = [d for d in os.listdir(modules_dir) if os.path.isdir(os.path.join(modules_dir, d))]
for kver in kvers:
logger.info(f"Adding kernel entry for {kver}...")
# Ensure initramfs exists. If not, generate it.
initrd_path_rel = f"boot/initramfs-{kver}.img"
initrd_full_path = os.path.join(mount_root, initrd_path_rel)
if not os.path.exists(initrd_full_path):
logger.info(f"Generating initramfs for {kver}...")
run_command(["chroot", mount_root, "dracut", "--force", f"/boot/initramfs-{kver}.img", kver])
kernel_image = f"/lib/modules/{kver}/vmlinuz"
initrd_arg = f"/boot/initramfs-{kver}.img"
# kernel-install add <version> <image> [initrd]
cmd = ["chroot", mount_root, "kernel-install", "add", kver, kernel_image]
if os.path.exists(os.path.join(mount_root, initrd_arg.lstrip("/"))):
cmd.append(initrd_arg)
run_command(cmd)
# Ensure all data is synced to disk
run_command(["sync"])
logger.info("System configuration complete.")