Merge pull request #1894 from medicalwei/factory-reset-fix-boots
factory-reset: remove EFI boot entry, retarget GRUB entry to chainload Reset Partition GRUB
This commit is contained in:
commit
5e5ef826d5
|
@ -31,7 +31,6 @@ from typing import Any, Dict, List, Optional
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
from curtin.config import merge_config
|
from curtin.config import merge_config
|
||||||
from curtin.util import get_efibootmgr, is_uefi_bootable
|
|
||||||
|
|
||||||
from subiquity.common.errorreport import ErrorReportKind
|
from subiquity.common.errorreport import ErrorReportKind
|
||||||
from subiquity.common.pkg import TargetPkg
|
from subiquity.common.pkg import TargetPkg
|
||||||
|
@ -509,18 +508,16 @@ class InstallController(SubiquityController):
|
||||||
return new_casper_uuid
|
return new_casper_uuid
|
||||||
|
|
||||||
@with_context(description="configuring grub menu entry for factory reset")
|
@with_context(description="configuring grub menu entry for factory reset")
|
||||||
async def configure_rp_boot_grub(self, context, rp: Partition, casper_uuid: str):
|
async def configure_rp_boot_grub(self, context, rp: Partition):
|
||||||
# Add a grub menu entry to boot from the RP
|
# Add a grub menu entry to boot from the RP
|
||||||
cp = await self.app.command_runner.run(
|
cp = await self.app.command_runner.run(
|
||||||
["lsblk", "-n", "-o", "UUID,PARTUUID", rp.path], capture=True
|
["lsblk", "-n", "-o", "UUID", rp.path], capture=True
|
||||||
)
|
)
|
||||||
fs_uuid, rp_uuid = cp.stdout.decode("ascii").strip().split()
|
fs_uuid = cp.stdout.decode("ascii").strip()
|
||||||
conf = grub_reset_conf.format(
|
conf = grub_reset_conf.format(
|
||||||
HEADER=generate_timestamped_header(),
|
HEADER=generate_timestamped_header(),
|
||||||
PARTITION=rp.number,
|
PARTITION=rp.number,
|
||||||
FS_UUID=fs_uuid,
|
FS_UUID=fs_uuid,
|
||||||
CASPER_UUID=casper_uuid,
|
|
||||||
RP_UUID=rp_uuid,
|
|
||||||
)
|
)
|
||||||
with open(self.tpath("etc/grub.d/99_reset"), "w") as fp:
|
with open(self.tpath("etc/grub.d/99_reset"), "w") as fp:
|
||||||
os.chmod(fp.fileno(), 0o755)
|
os.chmod(fp.fileno(), 0o755)
|
||||||
|
@ -536,74 +533,9 @@ class InstallController(SubiquityController):
|
||||||
private_mounts=False,
|
private_mounts=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
@with_context(description="configuring UEFI menu entry for factory reset")
|
|
||||||
async def configure_rp_boot_uefi(self, context, rp: Partition):
|
|
||||||
# Add an UEFI boot entry to point at the RP
|
|
||||||
# Details:
|
|
||||||
# 1. Do not leave duplicate entries
|
|
||||||
# 2. Do not leave the boot entry in BootOrder
|
|
||||||
# 3. Set BootNext to boot from RP in the reset-partition-only case.
|
|
||||||
state = await self.app.package_installer.install_pkg("efibootmgr")
|
|
||||||
if state != PackageInstallState.DONE:
|
|
||||||
raise RuntimeError("could not install efibootmgr")
|
|
||||||
efi_state_before = get_efibootmgr("/")
|
|
||||||
cmd = [
|
|
||||||
"efibootmgr",
|
|
||||||
"--create",
|
|
||||||
"--loader",
|
|
||||||
"\\EFI\\boot\\bootx64.efi",
|
|
||||||
"--disk",
|
|
||||||
rp.device.path,
|
|
||||||
"--part",
|
|
||||||
str(rp.number),
|
|
||||||
"--label",
|
|
||||||
"Restore Ubuntu to factory state",
|
|
||||||
]
|
|
||||||
await self.app.command_runner.run(cmd)
|
|
||||||
efi_state_after = get_efibootmgr("/")
|
|
||||||
new_bootnums = set(efi_state_after.entries) - set(efi_state_before.entries)
|
|
||||||
if not new_bootnums:
|
|
||||||
# Will probably only happen in dry-run mode.
|
|
||||||
return
|
|
||||||
new_bootnum = new_bootnums.pop()
|
|
||||||
new_entry = efi_state_after.entries[new_bootnum]
|
|
||||||
orig_num = None
|
|
||||||
for num, entry in efi_state_before.entries.items():
|
|
||||||
if entry.path == new_entry.path and entry.name == new_entry.name:
|
|
||||||
orig_num = num
|
|
||||||
if orig_num is not None:
|
|
||||||
cmd = [
|
|
||||||
"efibootmgr",
|
|
||||||
"--delete-bootnum",
|
|
||||||
"--bootnum",
|
|
||||||
new_bootnum,
|
|
||||||
]
|
|
||||||
rp_bootnum = orig_num
|
|
||||||
else:
|
|
||||||
cmd = [
|
|
||||||
"efibootmgr",
|
|
||||||
"--bootorder",
|
|
||||||
",".join(efi_state_before.order + [new_bootnum]),
|
|
||||||
]
|
|
||||||
rp_bootnum = new_bootnum
|
|
||||||
await self.app.command_runner.run(cmd)
|
|
||||||
if self.model.target is None:
|
|
||||||
cmd = [
|
|
||||||
"efibootmgr",
|
|
||||||
"--bootnext",
|
|
||||||
rp_bootnum,
|
|
||||||
]
|
|
||||||
await self.app.command_runner.run(cmd)
|
|
||||||
|
|
||||||
async def configure_rp_boot(self, context, rp: Partition, casper_uuid: str):
|
async def configure_rp_boot(self, context, rp: Partition, casper_uuid: str):
|
||||||
if self.model.target is not None and not self.opts.dry_run:
|
if self.model.target is not None and not self.opts.dry_run:
|
||||||
await self.configure_rp_boot_grub(
|
await self.configure_rp_boot_grub(context=context, rp=rp)
|
||||||
context=context, rp=rp, casper_uuid=casper_uuid
|
|
||||||
)
|
|
||||||
if self.app.opts.dry_run and not is_uefi_bootable():
|
|
||||||
# Can't even run efibootmgr in this case.
|
|
||||||
return
|
|
||||||
await self.configure_rp_boot_uefi(context=context, rp=rp)
|
|
||||||
|
|
||||||
async def maybe_configure_exiting_rp_boot(self, context):
|
async def maybe_configure_exiting_rp_boot(self, context):
|
||||||
# We are not creating a reset partition here if we are running
|
# We are not creating a reset partition here if we are running
|
||||||
|
@ -906,8 +838,7 @@ set -e
|
||||||
cat << EOF
|
cat << EOF
|
||||||
menuentry "Restore Ubuntu to factory state" {{
|
menuentry "Restore Ubuntu to factory state" {{
|
||||||
search --no-floppy --hint '(hd0,{PARTITION})' --set --fs-uuid {FS_UUID}
|
search --no-floppy --hint '(hd0,{PARTITION})' --set --fs-uuid {FS_UUID}
|
||||||
linux /casper/vmlinuz uuid={CASPER_UUID} rp-partuuid={RP_UUID} nopersistent
|
chainloader /EFI/boot/bootx64.efi
|
||||||
initrd /casper/initrd
|
|
||||||
}}
|
}}
|
||||||
EOF
|
EOF
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -19,7 +19,7 @@ import subprocess
|
||||||
import tempfile
|
import tempfile
|
||||||
import unittest
|
import unittest
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from unittest.mock import ANY, AsyncMock, Mock, call, mock_open, patch
|
from unittest.mock import ANY, AsyncMock, Mock, mock_open, patch
|
||||||
|
|
||||||
from curtin.util import EFIBootEntry, EFIBootState
|
from curtin.util import EFIBootEntry, EFIBootState
|
||||||
|
|
||||||
|
@ -262,116 +262,13 @@ class TestInstallController(unittest.IsolatedAsyncioTestCase):
|
||||||
app.package_installer.install_pkg.return_value = PackageInstallState.DONE
|
app.package_installer.install_pkg.return_value = PackageInstallState.DONE
|
||||||
fsm, self.part = make_model_and_partition()
|
fsm, self.part = make_model_and_partition()
|
||||||
|
|
||||||
@patch("subiquity.server.controllers.install.get_efibootmgr")
|
|
||||||
async def test_configure_rp_boot_uefi_add(self, m_get_efibootmgr):
|
|
||||||
m_get_efibootmgr.side_effect = iter([efi_state_no_rp, efi_state_with_rp])
|
|
||||||
self.setup_rp_test()
|
|
||||||
await self.controller.configure_rp_boot_uefi(rp=self.part)
|
|
||||||
calls = [
|
|
||||||
call(
|
|
||||||
[
|
|
||||||
"efibootmgr",
|
|
||||||
"--create",
|
|
||||||
"--loader",
|
|
||||||
"\\EFI\\boot\\bootx64.efi",
|
|
||||||
"--disk",
|
|
||||||
self.part.device.path,
|
|
||||||
"--part",
|
|
||||||
str(self.part.number),
|
|
||||||
"--label",
|
|
||||||
"Restore Ubuntu to factory state",
|
|
||||||
]
|
|
||||||
),
|
|
||||||
call(
|
|
||||||
[
|
|
||||||
"efibootmgr",
|
|
||||||
"--bootorder",
|
|
||||||
"0000,0002,0003",
|
|
||||||
]
|
|
||||||
),
|
|
||||||
]
|
|
||||||
self.run.assert_has_awaits(calls)
|
|
||||||
|
|
||||||
@patch("subiquity.server.controllers.install.get_efibootmgr")
|
|
||||||
async def test_configure_rp_boot_uefi_bootnext(self, m_get_efibootmgr):
|
|
||||||
m_get_efibootmgr.side_effect = iter([efi_state_no_rp, efi_state_with_rp])
|
|
||||||
self.setup_rp_test()
|
|
||||||
self.controller.app.base_model.target = None
|
|
||||||
await self.controller.configure_rp_boot_uefi(rp=self.part)
|
|
||||||
calls = [
|
|
||||||
call(
|
|
||||||
[
|
|
||||||
"efibootmgr",
|
|
||||||
"--create",
|
|
||||||
"--loader",
|
|
||||||
"\\EFI\\boot\\bootx64.efi",
|
|
||||||
"--disk",
|
|
||||||
self.part.device.path,
|
|
||||||
"--part",
|
|
||||||
str(self.part.number),
|
|
||||||
"--label",
|
|
||||||
"Restore Ubuntu to factory state",
|
|
||||||
]
|
|
||||||
),
|
|
||||||
call(
|
|
||||||
[
|
|
||||||
"efibootmgr",
|
|
||||||
"--bootorder",
|
|
||||||
"0000,0002,0003",
|
|
||||||
]
|
|
||||||
),
|
|
||||||
call(
|
|
||||||
[
|
|
||||||
"efibootmgr",
|
|
||||||
"--bootnext",
|
|
||||||
"0003",
|
|
||||||
]
|
|
||||||
),
|
|
||||||
]
|
|
||||||
self.run.assert_has_awaits(calls)
|
|
||||||
|
|
||||||
@patch("subiquity.server.controllers.install.get_efibootmgr")
|
|
||||||
async def test_configure_rp_boot_uefi_dup(self, m_get_efibootmgr):
|
|
||||||
m_get_efibootmgr.side_effect = iter([efi_state_with_rp, efi_state_with_dup_rp])
|
|
||||||
self.setup_rp_test()
|
|
||||||
await self.controller.configure_rp_boot_uefi(rp=self.part)
|
|
||||||
calls = [
|
|
||||||
call(
|
|
||||||
[
|
|
||||||
"efibootmgr",
|
|
||||||
"--create",
|
|
||||||
"--loader",
|
|
||||||
"\\EFI\\boot\\bootx64.efi",
|
|
||||||
"--disk",
|
|
||||||
self.part.device.path,
|
|
||||||
"--part",
|
|
||||||
str(self.part.number),
|
|
||||||
"--label",
|
|
||||||
"Restore Ubuntu to factory state",
|
|
||||||
]
|
|
||||||
),
|
|
||||||
call(
|
|
||||||
[
|
|
||||||
"efibootmgr",
|
|
||||||
"--delete-bootnum",
|
|
||||||
"--bootnum",
|
|
||||||
"0004",
|
|
||||||
]
|
|
||||||
),
|
|
||||||
]
|
|
||||||
self.run.assert_has_awaits(calls)
|
|
||||||
|
|
||||||
async def test_configure_rp_boot_grub(self):
|
async def test_configure_rp_boot_grub(self):
|
||||||
fsuuid, partuuid = "fsuuid", "partuuid"
|
fsuuid, partuuid = "fsuuid", "partuuid"
|
||||||
self.setup_rp_test(f"{fsuuid}\t{partuuid}".encode("ascii"))
|
self.setup_rp_test(f"{fsuuid}\t{partuuid}".encode("ascii"))
|
||||||
await self.controller.configure_rp_boot_grub(
|
await self.controller.configure_rp_boot_grub(rp=self.part)
|
||||||
rp=self.part, casper_uuid="casper-uuid"
|
|
||||||
)
|
|
||||||
with open(self.controller.tpath("etc/grub.d/99_reset")) as fp:
|
with open(self.controller.tpath("etc/grub.d/99_reset")) as fp:
|
||||||
cfg = fp.read()
|
cfg = fp.read()
|
||||||
self.assertIn("--fs-uuid fsuuid", cfg)
|
self.assertIn("--fs-uuid fsuuid", cfg)
|
||||||
self.assertIn("rp-partuuid=partuuid", cfg)
|
|
||||||
self.assertIn("uuid=casper-uuid", cfg)
|
|
||||||
|
|
||||||
@patch("platform.machine", return_value="s390x")
|
@patch("platform.machine", return_value="s390x")
|
||||||
@patch("subiquity.server.controllers.install.arun_command")
|
@patch("subiquity.server.controllers.install.arun_command")
|
||||||
|
|
Loading…
Reference in New Issue